import {
  ApolloCache,
  ApolloClient,
  ApolloLink,
  InMemoryCache,
  NormalizedCacheObject,
} from '@apollo/client/core';
import { RestLink } from 'apollo-link-rest';

import { GraphQLErrorMatchHandler } from './graphql-error-handler';

export type RequestHeadersAugmentor = (headers: Record<string, any>) => Record<string, any>;

export interface RestApiClientConfig {
  ssrMode: boolean;
  connectToDevTools: boolean;
  url: string;
  subscriptionUrl?: string;
  batchEnabled: boolean;
  headersAugmentor?: RequestHeadersAugmentor;
  graphQLErrorHandlers?: GraphQLErrorMatchHandler[];
}

const loadHeaders = (config: RestApiClientConfig): Record<string, any> => {
  let headers: Record<string, any> = {};
  headers['Content-Type'] = 'application/json;charset=UTF-8';
  headers['Accept-Language'] = 'zh-CN';
  if (config.headersAugmentor) {
    headers = config.headersAugmentor(headers);
  }
  return headers;
};

const createRestLink = (config: RestApiClientConfig): ApolloLink => {
  return new RestLink({ uri: config.url });
};

const createMiddleware = (config: RestApiClientConfig): ApolloLink => {
  return new ApolloLink((operation, forward) => {
    operation.setContext({
      headers: loadHeaders(config),
    });
    return forward(operation);
  });
};

const createLink = (config: RestApiClientConfig): ApolloLink => {
  const middleware = createMiddleware(config);
  const restLink = createRestLink(config);
  return ApolloLink.from([middleware, restLink]);
};

export const createRestClient = (
  config: RestApiClientConfig
): {
  restClient: ApolloClient<any>;
  cache: ApolloCache<NormalizedCacheObject>;
} => {
  const link = createLink(config);
  const cache = new InMemoryCache({
    resultCaching: true,
  });
  const restClient = new ApolloClient({
    link,
    cache,
    ssrMode: config.ssrMode,
    defaultOptions: {
      query: {
        notifyOnNetworkStatusChange: true,
        fetchPolicy: 'no-cache',
      },
      mutate: {
        fetchPolicy: 'no-cache',
        keepRootFields: true,
      },
      watchQuery: {
        notifyOnNetworkStatusChange: true,
        fetchPolicy: 'no-cache',
        nextFetchPolicy: 'no-cache',
        returnPartialData: true,
      },
    },
    connectToDevTools: config.connectToDevTools,
  });
  return { restClient, cache };
};
